#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <atomic>
#include <unistd.h>
#include <fstream>

#include "../Logs/Uselog.h"

using namespace LogModule;

/*
在这里进行日志落地模块的扩展 -- 根据时间进行滚动，而不是根据文件大小进行滚动
实现思路为：
    1. 定义出一个滚动时间片的大小 -- 比如1s、1分钟(60秒)、1小时(3600秒)
    2. 以当前系统时间，取模时间片大小，可以得出当前系统时间处于第几个时间片
    3. 如果等到当前系统时间取模滚动时间片，不等于之前的结果，那么就说明过了特定的时间
    4. 过了特定的时间，那么就重新创建日志文件，向新的日志文件中进行写入操作
*/
enum class TimeGap
{
    // 为了方便传入的时间，设计一个枚举类型
    GAP_SECOND, // 秒
    GAP_MINUTE, // 分钟
    GAP_HOUR,   // 小时
    GAP_DAY     // 一天
};

class RollByTimeLogSink : public LogSink
{
public:
    RollByTimeLogSink(const std::string &basename, TimeGap timegap)
        : _basename(basename)
    {
        // 1. 设置好时间片
        switch (timegap)
        {
        case TimeGap::GAP_SECOND:
            _gap_size = 1;
            break;
        case TimeGap::GAP_MINUTE:
            _gap_size = 60;
            break;
        case TimeGap::GAP_HOUR:
            _gap_size = 3600;
            break;
        case TimeGap::GAP_DAY:
            _gap_size = 3600 * 60;
            break;
        }
        // 2. 也要创建文件
        time_t curtime = UtilModule::Date::Now();
        // 注意：任何数%1，都=0，测试检查出来的
        _cur_gap = _gap_size == 1 ? curtime : curtime % _gap_size;
        std::string filename = CreateNewFile();
        UtilModule::File::CreateDirectory(UtilModule::File::Path(filename));
        _ofs.open(filename);
        assert(_ofs.is_open());
    }
    void Log(const char *data, size_t len) override
    {
        // 1. 当时间片变化时，才会创建新的文件
        time_t curtime = UtilModule::Date::Now();
        if ((_gap_size == 1 ? curtime : curtime % _gap_size) != _cur_gap)
        {
            // 创建新的文件，不要忘了：关闭之前打开的文件、重设当前时间片
            _ofs.close();
            _cur_gap = _cur_gap = _gap_size == 1 ? curtime : curtime % _gap_size;
            std::string filename = CreateNewFile();
            UtilModule::File::CreateDirectory(UtilModule::File::Path(filename));
            _ofs.open(filename);
            assert(_ofs.is_open());
        }
        _ofs.write(data, len);
        assert(_ofs.good());
    }

private:
    // 当时间片变化时，创建文件
    std::string CreateNewFile()
    {
        std::stringstream filename;
        time_t t = UtilModule::Date::Now();
        struct tm ltime;
        localtime_r(&t, &ltime);
        filename << _basename;
        filename << ltime.tm_year + 1900; // 从1900年开始为第0年，所以要加上1900
        filename << ltime.tm_mon + 1;     // 从0开始计算，所以要加1才是现实的月份
        filename << ltime.tm_mday;
        filename << ltime.tm_hour;
        filename << ltime.tm_min;
        filename << ltime.tm_sec;
        filename << ".log";
        return filename.str();
    }

private:
    std::string _basename; // 同样的，也需要传入创建的文件
    std::ofstream _ofs;
    size_t _gap_size; // 时间片的大小
    size_t _cur_gap;  // 当前所处的是第几个时间片
};

void text_log()
{
    // 获取日志器管理器单例
    Logger::ptr logger = LoggerManager::GetInstance().GetLogger("test");
    logger->Debug("%s - %d", "日志器管理器测试", 11);
    logger->Warn("%s - %d", "日志器管理器测试", 11);
    logger->Error("%s - %d", "日志器管理器测试", 11);
    logger->Fatal("%s - %d", "日志器管理器测试", 11);
}

int main()
{
    // main函数中创建日志器
    LoggerBuilder::ptr builder = std::make_shared<GlobalLoggerBuilder>();
    builder->BuildLoggerName("test");
    builder->build();
    text_log();

    // 使用默认日志器
    DEBUG("%s - %d", "日志器管理器测试", 11);
    INFO("%s - %d", "日志器管理器测试", 11);
    WARN("%s - %d", "日志器管理器测试", 11);
    ERROR("%s - %d", "日志器管理器测试", 11);
    FATAL("%s - %d", "日志器管理器测试", 11);
    return 0;
}

// // 日志器管理器的测试
// // 在main函数中创建日志器，添加到日志器管理器中
// // 在text_log函数中使用全局的日志器管理器
// void text_log()
// {
//     // 获取日志器管理器单例
//     Logger::ptr logger = LoggerManager::GetInstance().GetLogger("test");
//     logger->Debug("main.cc", 154, "%s - %d", "日志器管理器测试", 11);
//     logger->Warn("main.cc", 154, "%s - %d", "日志器管理器测试", 11);
//     logger->Error("main.cc", 154, "%s - %d", "日志器管理器测试", 11);
//     logger->Fatal("main.cc", 154, "%s - %d", "日志器管理器测试", 11);
// }

// int main()
// {
//     // main函数中创建日志器
//     LoggerBuilder::ptr builder = std::make_shared<GlobalLoggerBuilder>();
//     builder->BuildLoggerName("test");
//     builder->BuildLoggerLevel(LogLevel::Level::ERROR);
//     builder->build();
//     text_log();
//     return 0;
// }

// int main()
// {
//     // // 创建了抽象类，而不是派生类
//     // LoggerBuilder::ptr builder = std::make_shared<LoggerBuilder>();
//     LoggerBuilder::ptr builder = std::make_shared<LocalLoggerBuilder>();
//     //builder->BuildSetAsyncUnsafe();
//     builder->BuildLoggerName("root");
//     builder->BuildSink<FileLogSink>("./log/async.txt");
//     builder->BuildSink<StdOutLogSink>();
//     // 设置为异步模式
//     builder->BulidLoggerType(LoggerType::LOGGER_ASYNC);
//     Logger::ptr logger = builder->build();
    
//     for(int i = 0; i<5000; i++)
//     {
//         logger->Debug("main.cc", 154, "%s - %d - %d", "异步模式测试", 11, i);
//         usleep(1000);
//     }
    
//     // 给异步线程足够的时间来处理所有日志
//     std::cout << "等待异步线程处理所有日志..." << std::endl;
//     sleep(3);
//     return 0;
// }

// int main()
// {
//     // 测试用例 -- 读取文件中的数据，一点一点写入缓冲区，最终将缓冲区数据写入文件
//     //  判断原来的文件与生成的文件是否相同
//     std::ifstream ifs("./log/log.txt", std::ios::binary);
//     if (!ifs.is_open()) return -1;
//     // read需要知道读取多少大小
//     ifs.seekg(0, std::ios::end); // 读写位置跳转到文件末尾
//     size_t fsize = ifs.tellg();  // 告诉我当前位置相对于起始位置的偏移量
//     ifs.seekg(0, std::ios::beg); // 读写位置移动到文件开头
//     std::string data;
//     data.resize(fsize);        // 先调整字符串大小以容纳所有数据
//     ifs.read(&data[0], fsize); // 不能是data.c_str()，因为返回的是const char*，肯定不能是右值
//     if (!ifs.good()) return -1;
//     ifs.close();

//     // 1. 缓冲区的使用如下：
//     Buffer buffer;
//     for (int i = 0; i < data.size(); i++)
//     {
//         buffer.Push(&data[i], 1);
//     }
//     // 2. 将缓冲区中的数据写入到文件：
//     std::ofstream ofs("./log/copylog.txt", std::ios::binary);
//     // 注意：不能使用for(int i = 0; i<buffer.ReadAbleSize(); i++)
//     // 因为随着从buffer中不断获取数据，ReadAbleSize会不断减小！因为可读数据在减少！
//     while (buffer.ReadAbleSize() > 0)
//     {
//         ofs.write(buffer.Begin(), 1);
//         buffer.MoveReader(1);
//     }

//     return 0;
// }

// int main()
// {
//     // 1. 工厂模式测试，只需要创建一个工厂，填写需要的信息，就可以使用了
//     LoggerBuilder::ptr syncptr = std::make_shared<LocalLoggerBuilder>();
//     syncptr->BuildLoggerName("root");
//     syncptr->BuildLoggerLevel(LogLevel::Level::DEBUG);
//     syncptr->BuildSink<FileLogSink>("./log/log.txt");
//     Logger::ptr loggerptr = syncptr->build();
//     // 2. 日志的打印使用
//     loggerptr->Debug("main.cc", 109, "%s - %d", "工厂模式测试", 11);
//     size_t cursize = 0;
//     while(cursize < 1024*1024*10)
//     {
//         loggerptr->Fatal("main.cc", 109, "%s - %d", "工厂模式测试", 11);
//         cursize += 30;
//     }

//     return 0;
// }

// int main()
// {
//     // 同步操作，必须提供：
//     // logger_name 日志器名称   limit_level 限制等级
//     // formatter 格式类         sinks 日志消息落地数组
//     // 1. 日志器名称
//     std::string logger_name = "root";
//     // 2. 限制等级
//     LogLevel::Level limit_level = LogLevel::Level::WARN;
//     // 3. 要知道按照什么格式输出，默认格式[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
//     Formatter::ptr formatter = std::make_shared<Formatter>();
//     // 4. 需要将日志消息落地需求填入到数组中
//     std::vector<LogSink::ptr> sinks;
//     LogSink::ptr stdoutptr = SinkFactory::Create<StdOutLogSink>();
//     LogSink::ptr fileptr = SinkFactory::Create<FileLogSink>("./log/log.txt");
//     LogSink::ptr rollsizeptr = SinkFactory::Create<RollBySizeLogSink>("./log/log-", 1024 * 1024);
//     sinks.push_back(stdoutptr);
//     sinks.push_back(fileptr);
//     sinks.push_back(rollsizeptr);
//     // 5. 最后就可以使用同步操作了
//     Logger::ptr loggerptr = std::make_shared<SyncLogger>(logger_name, limit_level, formatter, sinks);
//     // 至少需要提供：file 文件名   line 行号   format 格式   ... 有效载荷，对应前面的格式
//     //                               格式和有效载荷对应说明：%s - %d 对应 "有效载荷" - 111
//     loggerptr->Debug("main.cc", 11, "%s - %d", "Debug测试", 111);
//     loggerptr->Info("main.cc", 11, "%s - %d", "Debug测试", 111);
//     loggerptr->Warn("main.cc", 11, "%s - %d", "Debug测试", 111);
//     loggerptr->Error("main.cc", 11, "%s - %d", "Debug测试", 111);
//     loggerptr->Fatal("main.cc", 11, "%s - %d", "Debug测试", 111);
//     return 0;
// }

// int main()
// {
//     // 1. 先有日志输出信息、再有日志信息以什么格式输出
//     LogMessage msg(53, "main.cc", LogLevel::Level::FATAL, "套接字创建失败", "root");
//     Formatter fmt;
//     std::string ret = fmt.Format(msg);

//     // 2. 直接使用工厂，创建落地类对象
//     LogSink::ptr rollbytimeptr = SinkFactory::Create<RollByTimeLogSink>("./log/rollbytime-", TimeGap::GAP_SECOND);
//     time_t old = UtilModule::Date::Now();
//     while (UtilModule::Date::Now() < old + 5)
//     {
//         // 写入5秒，测试是否可以成功写入，而且可以成功滚动
//         rollbytimeptr->Log(ret.c_str(), ret.size());
//         usleep(1000);
//     }
//     return 0;
// }

// int main()
// {
//     // 1. 先有日志输出的信息
//     LogMessage msg(53, "main.cc", LogLevel::Level::FATAL, "套接字创建失败", "root");
//     // 2. 按照指定格式进行输出，默认输出格式：[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
//     Formatter fmt;
//     std::string ret = fmt.Format(msg);

//     // 3. 日志不同落地策略测试
//     // 3.1 测试1：标准输出测试
//     // 此时直接使用工厂，就可以实现特定的落地方案了！
//     LogSink::ptr stoutptr = SinkFactory::Create<StdOutLogSink>();
//     stoutptr->Log(ret.c_str(), ret.size());

//     // 3.2 测试2：指定文件测试
//     LogSink::ptr fileptr = SinkFactory::Create<FileLogSink>("./log/log.txt");
//     fileptr->Log(ret.c_str(), ret.size());

//     // 3.3 测试3：滚动文件测试
//     // 1024 -- 1KB，1024*1024 -- 1MB
//     // 指定日志文件只要超过1MB就进行滚动
//     LogSink::ptr rollbysizeptr = SinkFactory::Create<RollBySizeLogSink>("./log/rolllog-", 1024 * 1024);
//     rollbysizeptr->Log(ret.c_str(), ret.size());

//     // 3.4 测试4：滚动文件滚动测试
//     size_t currsize = 0;
//     while (currsize < 1024 * 1024 * 10)
//     {
//         rollbysizeptr->Log(ret.c_str(), ret.size());
//         currsize += ret.size();
//     }
//     return 0;
// }

// int main()
// {
//     // 1. 先要有日志输出的信息
//     // 构造函数，需要传入的有：
//     // line 行号           file 文件名称          level 日志等级
//     // payload 有效载荷    name 日志器名称
//     LogMessage msg(53, "main.cc", LogLevel::Level::FATAL, "套接字创建失败", "root");

//     // 2. 创建日志格式化类 -- 想要日志使用什么格式输出
//     // 默认格式为[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n
//     // 自定义格式，自己创建Formatter时传参即可
//     Formatter fmt;
//     std::string ret = fmt.Format(msg);
//     std::cout << ret;

//     // 3. 边缘情况1：ab%%cd[%d{%H:%M:%S}]
//     Formatter fmt1("ab%%cd[%d{%H:%M:%S}]");
//     ret = fmt1.Format(msg);
//     std::cout << ret << std::endl;

//     // 4. 边缘情况2：%c 开头
//     Formatter fmt2("%cab%%cd[%d{%H:%M:%S}]%n");
//     ret = fmt2.Format(msg);
//     std::cout << ret;

//     // 5. 边缘情况3：{}匹配错误 %%cd[%d{%H:%M:%S]%n
//     Formatter fmt3("%%cd[%d{%H:%M:%S]%n");
//     ret = fmt3.Format(msg);
//     std::cout << ret << std::endl;

//     // 6. 边缘情况4：%后面跟的格式化信息不存在 -- 转化为other，什么也不加
//     Formatter fmt4("%a%bcd[%d{%H:%M:%S}]%n");
//     ret = fmt4.Format(msg);
//     std::cout << ret;

//     // 7. 边缘情况5：%后面什么都没有
//     Formatter fmt5("%");
//     ret = fmt5.Format(msg);
//     std::cout << ret << std::endl;
//     return 0;
// }

// int main()
// {
//     std::cout << LogLevel::ToString(LogLevel::Level::DEBUG) << std::endl;
//     std::cout << LogLevel::ToString(LogLevel::Level::INFO) << std::endl;
//     std::cout << LogLevel::ToString(LogLevel::Level::WARN) << std::endl;
//     std::cout << LogLevel::ToString(LogLevel::Level::ERROR) << std::endl;
//     std::cout << LogLevel::ToString(LogLevel::Level::FATAL) << std::endl;
//     std::cout << LogLevel::ToString(LogLevel::Level::OFF) << std::endl;
//     std::cout << LogLevel::ToString(LogLevel::Level::UNKNOW) << std::endl;

//     return 0;
// }

// int main()
// {
//     std::cout << UtilModule::Date::Now() << std::endl;
//     std::cout << UtilModule::File::Exists("./a/b/c.txt") << std::endl;
//     std::cout << UtilModule::File::Path("./a/b/c.txt") << std::endl;
//     UtilModule::File::CreateDirectory("./a/b/c.txt");
//     std::cout << UtilModule::File::Exists("./a/b/c.txt") << std::endl;

//     return 0;
// }